Skip to content

docs(platform-objects): widen sys_email.error description to cover pre-delivery rejections - #15276

Merged
zhuangjianguo merged 8 commits into
mainfrom
claude/issue-14372-sys-email-error-description
Sep 4, 2026
Merged

docs(platform-objects): widen sys_email.error description to cover pre-delivery rejections#15276
zhuangjianguo merged 8 commits into
mainfrom
claude/issue-14372-sys-email-error-description

Conversation

@zhuangjianguo

@zhuangjianguo zhuangjianguo commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #14372

What

sys_email.error (packages/platform-objects/src/audit/sys-email.object.ts) was declared as:

Transport error message when status=failed

Since #14371, EmailService.recordRejectedMessage (plugin-email/src/email-service.ts:677/:852) also writes status: 'failed' rows for messages normalizeMessage rejects before they reach a transport (unsendable from, no recipient, no subject, no body), prefixed rejected before delivery: (:875). The declared field description was narrower than what the column now holds.

Widened to the wording settled in triage (issue comment 5504375428, reconfirmed in 5536386432/5537220287):

Why the message failed — a transport error, or the validation that rejected it before delivery.

How

Edited only the declaration (sys-email.object.ts:195) and regenerated the platform-objects i18n bundle with its own tooling — node scripts/check-i18n-bundles.mjs --write — rather than hand-editing en.objects.generated.ts (its help: key at line 2479, per the source-of-truth guardrail in this repo's docs). Confirmed the generated help: string actually changed and that check:i18n re-verifies clean afterward.

The translated locales (zh-CN/ja-JP/es-ES) keep their existing (now-stale relative to the new English) human translations by the extractor's own merge-mode design — that is expected behaviour for this bundle (untouched keys are not drift) and outside this card's scope.

No behaviour change: no schema key added/removed, recordRejectedMessage untouched, no column change.

Net change: exactly 3 files, +25/-2 (matches GitHub's own changed_files/additions/deletions on this PR as of HEAD 71590c470) — .changeset/sys-email-error-description-widen.md, packages/platform-objects/src/apps/translations/en.objects.generated.ts, packages/platform-objects/src/audit/sys-email.object.ts. The commit list carries extra entries from a merge-base hiccup fixed mid-review (see note at the bottom) — those are not part of this PR's actual diff.

Verification

  • git grep positive controls on origin/main @ 97bcd99e: recordRejectedMessage (email-service.ts:677,:852) and "rejected before delivery: " (:875) both fire — premise re-confirmed live (was 0 on 2026-09-02, non-zero now).
  • pnpm --filter @objectstack/platform-objects test — 33/33 files, 518/518 tests pass.
  • pnpm --filter @objectstack/platform-objects typecheck — clean.
  • pnpm check:i18n (full, 9 packages) — green; filtered re-run — platform-objects in sync (11 bundles).
  • pnpm check:i18n-stale-fill — green (this is the gate that would have caught a stranded stale leaf on a revised source string; 0 stale-fill leaves).
  • No other package/bundle in the repo carries the old string (git grep clean).

Local gate families (per family, re-derived with node scripts/pm/dispatch-gates.mjs, no path args, on the corrected 3-file surface — merge base 460134af8 — at HEAD 71590c470)

37/37 green:

gate exit
node scripts/check-adr-0087-registration.mjs --self-test 0
node scripts/check-changeset-no-major.mjs --self-test 0
node scripts/check-ci-filter-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs --self-test 0
node scripts/check-comment-mask-adoption.mjs 0
node scripts/check-comment-mask-adoption.mjs --self-test 0
node scripts/check-comment-mask-corpus.mjs 0
node scripts/check-empty-changeset.mjs --self-test 0
node scripts/check-keyed-text-bounds.mjs 0
node scripts/check-keyed-text-bounds.mjs --self-test 0
node scripts/check-plugin-teardown-shape.mjs 0
node scripts/check-plugin-teardown-shape.mjs --self-test 0
node scripts/check-system-context-census.mjs 0
node scripts/check-system-context-census.mjs --self-test 0
node scripts/check-undeclared-dep-imports.mjs 0
node scripts/check-undeclared-dep-imports.mjs --self-test 0
node scripts/docs-audit/check-affected-docs.mjs 0
node scripts/docs-audit/check-drift-comment.mjs 0
node scripts/pm/release-rehearsal-clone.mjs --self-test 0
pnpm check:changeset-gate-self-tests 0
pnpm check:cross-package-test-inputs 0
pnpm check:doc-authoring 0
pnpm check:dual-build-cjs-loads 0
pnpm check:i18n 0
pnpm check:i18n-stale-fill 0
pnpm check:logger-receiver-detach 0
pnpm check:nul-bytes 0
pnpm check:objectui-changeset 0
pnpm check:page-declaration-shape 0
pnpm check:pm-half-states 0
pnpm check:published-files 0
pnpm check:refd-timer-probe 0
pnpm check:slot-lookup 0
pnpm check:test-source-alias 0
pnpm check:type-source-resolution 0
pnpm check:watch-hint-literal 0

(29 matched by path + 2 by change kind [check:i18n, check:i18n-stale-fill, since this diff revises an existing source string] + 6 declared whole-tree.) pnpm check:dual-build-cjs-loads needs a full pnpm build first (it refuses with exit 3 and states nothing was measured otherwise) — done before this run.

Changeset

@objectstack/platform-objects ships this description as field metadata (Studio reads it as the field's help text), so this is not comment-only — added a patch changeset.

Note: merge-base correction mid-review

An earlier push in this PR's history briefly desynced its merge-base from origin/main (a git filter-branch used to clean up commit-message trailers rewrote commits that had been merged in from origin/main, replacing them with content-identical-but-different-SHA copies — so git merge-base origin/main HEAD stopped advancing past this branch's original fork point, and GitHub's three-dot diff/file-count briefly over-reported). Fixed by merging the current origin/main in again (no rebase, no force-push) — git merge-base origin/main HEAD now correctly resolves to origin/main's real tip, and the three-dot and two-dot diffs agree on exactly the 3 files above. The gate results in this body were re-derived and re-run fresh after that fix, superseding an earlier report-comment reading on the issue that (harmlessly, since content never changed) predates it.


Generated by Claude Code

os-litant and others added 7 commits September 4, 2026 06:57
…ndeclared request keys are compile errors (#15215)

* wip(runtime): type the packages-domain protocol service handle

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

* wip(runtime): add the packages-domain protocol handle typing pin

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

* chore(changeset): patch note for the packages-domain protocol handle typing

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

* docs(permissions): re-anchor the system-context census rows moved by the typing block

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

* docs(permissions): regenerate the system-context census from the merged tree

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

---------

Co-authored-by: Claude <noreply@anthropic.com>
…15220)

* fix(objectql): publish the record's organization on every DataEvent (#14970)

`DataEventSchema.organizationId` was declared and published by the spec half
but populated by nothing, so every `data.record.*` event went out with the key
absent — which the contract requires a consumer to read as "this record is
behind no organization wall". `publishDataEvent` now resolves it from the row
itself: the written record on `created`, the post-state on `updated`, and the
by-id branch's already-read pre-image on `deleted`, so no per-event read is
bought.

The record's organization, never `ExecutionContext.tenantId` — that is the
caller's active org, and the two diverge on exactly the system/unscoped write
this key most needs to label correctly. Absence keeps one spelling: the key is
omitted, never `''` (which the schema refuses outright, dropping the whole
event) and never an explicit `undefined` (which survives `parse` as a present
key).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ

* docs(permissions): re-anchor the system-context census after the engine line shift

Mechanical repair by `node scripts/check-system-context-census.mjs --fix`, the
only correct writer for this table. Pure line rot: the `eventOrganizationId`
helper and its threading shifted every later line in `packages/objectql/src/engine.ts`,
so 14 anchors (15 citation sites — one source line is cited twice) pointed at
the wrong lines.

No population and no classification change: still 106 elevation read sites in
20 packages across 45 files, all anchored; 140 anchors resolve, 27 declared
non-read — the same figures as before the shift. `--fix` did not refuse, and
the diff is digits and nothing else (12 lines added, 12 removed, identical once
digits are stripped).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ

---------

Co-authored-by: Claude <noreply@anthropic.com>
…aude/ branches (#15224)

* ci(reaper): arm the merged-branch reaper for scheduled deletion of claude/ branches

Flips .github/workflows/merged-branch-reaper.yml from report-only to the
scheduled weekly deletion the maintainer ruled on 2026-09-04 (issue #12771,
decision batch #30), reaffirming the 2026-08-31 ruling under the base-ref
guard PR #15144 landed.

Deletion is a SEPARATE job (`reap`), because `permissions:` is scoped per
job. `sweep` keeps `contents: read` + `pull-requests: read` and remains
structurally incapable of deleting a ref; `reap` holds the only
`contents: write` in the file, consumes the `reapable` list `sweep`
publishes as a job output, and computes no classification of its own.

Fences:
- `reap` never runs on `pull_request` — the self-exercising run stays a dry
  run — and its `if:` is an allowlist of `schedule` plus a
  `workflow_dispatch` on which the operator explicitly set `dry_run: false`.
- the new `dry_run` workflow_dispatch input defaults to true, so the manual
  path is fail-closed.
- the base-ref guard, `PREFIX`, `BASE_REF`, the grace window, the schedule
  and the `is-ancestor` prohibition are all untouched.
- the whole deletion list is printed to the run log before the first delete.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk

* test(reaper): pin the deletion hand-off and fence the delete job structurally

The contract harness drives the `sweep` classifier and can say nothing about
the job that deletes — deletion deliberately lives outside the extracted
script, so what the harness judges stays a classification rather than an
action. Two additions close that gap.

1. The hand-off. `sweep` now publishes `reapable_branches`, the
   machine-readable half of the list it prints, and `reap` consumes that and
   nothing else. Scenarios G1/G2/R1 pin that the list EQUALS the reapable
   bucket — same members, same order — over a population carrying one branch
   in every bucket, and mutations M13/M14 drive both directions red (held
   branches leaking in; the list not published at all).

2. The fence. `reapFenceFailures()` parses the shipped YAML and asserts the
   delete job's structure: its `if:` excludes `pull_request` and gates
   `workflow_dispatch` on `inputs.dry_run == false`; it declares
   `contents: write` and is the ONLY job in the file that does; the top-level
   grant stays `contents: read`; it still `needs: sweep`. New self-test
   battery 6 drives six mutations of the workflow text to red, each asserting
   its anchor was present first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk

* ci(reaper): put every excluded bucket on the run-log audit line

The maintainer's ruling names the run log as the audit trail, and the notice
line named three of the seven buckets — reapable, mergedElsewhere, noPr. The
other four (open, closedUnmerged, grace, protectedBranch) lived only in the
step summary and the uploaded artifact, so the log alone could not answer
"what did it hold back, and why".

Also retires two strings that stopped being true when the reaper was armed:
the summary heading said "DRY RUN. Nothing was deleted." of a run that may
now delete in a later job, and the notice said "Nothing was deleted" of the
whole run rather than of this job. Both now speak for the `sweep` job only,
which is the thing they were ever really asserting — its token grant is
`contents: read` and that has not changed.

No classification changed: the buckets, the guard, the grace window and the
step outputs are byte-identical.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk

---------

Co-authored-by: Claude <noreply@anthropic.com>
…the TS2341 x3 it hid (#15152)

* wip: onboard service-automation typecheck, fix TS2341 residue

* wip: onboarding gate registry entry + changeset

* fix(scripts): re-measure this entry's provenance totals on the merged tree

The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:

  no `typecheck` script  absent   120 programs / 293 pairs
  names tsconfig.json    absent   120 programs / 293 pairs
  names tsconfig.test    PRESENT  121 programs / 302 pairs
  names both (the card)  PRESENT  121 programs / 302 pairs

  before  59 of 78 packages, 120 programs, 293 pairs, 19 clean
  after   60 of 78 packages, 121 programs, 302 pairs, 18 clean

The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y

---------

Co-authored-by: Claude <noreply@anthropic.com>
…e-delivery rejections

`sys_email.error` was declared as "Transport error message when status=failed",
but since #14371 EmailService.recordRejectedMessage also writes status=failed
rows for messages rejected by normalizeMessage before they reach a transport
(prefixed "rejected before delivery: ..."). The declared field help was
narrower than what the column actually holds.

Widen the description (wording settled in triage, issue comment 5504375428)
and regenerate the platform-objects i18n bundle with its own tooling
(node scripts/check-i18n-bundles.mjs --write) rather than hand-editing the
generated file.


Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…n widening

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 2 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/environment-variables.mdx (via sys_email (symbol, a field of const object enObjects))
  • content/docs/kernel/runtime-services/sms-service.mdx (via sys_email (symbol, a field of const object enObjects))
  • content/docs/plugins/packages.mdx (via sys_email (symbol, a field of const object enObjects))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via sys_email (symbol, a field of const object enObjects))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 460134af85f7ab2cf68abc62f1bbb9783b8899ddpackageMentionDocs.

Which tree this was computed on

This run read content/docs from c1e84067765fbc5ab4e1fbadd460b173dbd08b20 — the merge of head 71590c4709a91ceada04ce9f8f929b7e32e002be into base 460134af85f7ab2cf68abc62f1bbb9783b8899dd, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c1e84067765fbc5ab4e1fbadd460b173dbd08b20 && git checkout c1e84067765fbc5ab4e1fbadd460b173dbd08b20
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 460134af85f7ab2cf68abc62f1bbb9783b8899dd 71590c4709a91ceada04ce9f8f929b7e32e002be && git checkout -B drift-repro 460134af85f7ab2cf68abc62f1bbb9783b8899dd && git merge --no-ff 71590c4709a91ceada04ce9f8f929b7e32e002be

node scripts/docs-audit/affected-docs.mjs --json 460134af85f7ab2cf68abc62f1bbb9783b8899dd

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 460134af85f7ab2cf68abc62f1bbb9783b8899dd → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sys_email.error description says "transport error" but the column now also carries pre-delivery rejections

4 participants